//
//  WXMagicNumber.m
//  数字变化动效
//
//  Created by tianlong on 2018/6/5.
//  Copyright © 2018年 tianlong. All rights reserved.
//

#import "WXMagicNumber.h"

#define AnimationManager [WXAnimationNumber shareInstance]

@interface WXMagicNumber ()
@property (strong, nonatomic) CADisplayLink *displayLink;
@property (assign, nonatomic) MagicAnimationStyle style;
@property (assign, nonatomic) CFTimeInterval duration;
@property (assign, nonatomic) CFTimeInterval lastTime;
@property (assign, nonatomic) CFTimeInterval progressTime;
@property (assign, nonatomic) CGFloat startNumber;
@property (assign, nonatomic) CGFloat endNumber;
@property (assign, nonatomic) BufferFunction bufferFunction;
@property (copy, nonatomic) void (^progressBlock)(CGFloat number,CGFloat percent);
@end

@implementation WXMagicNumber

+ (instancetype)number {
    return [[WXMagicNumber alloc] init];
}

#pragma mark - 动画核心代码
- (void)startDisplayLink{
    if (self.displayLink) [self stopDisplayLink];
    self.displayLink = [CADisplayLink displayLinkWithTarget:self
                                                   selector:@selector(runAnimation:)];
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop]
                           forMode:NSDefaultRunLoopMode];
}

- (void)stopDisplayLink{
    [self.displayLink invalidate];
    self.displayLink = nil;
    self.progressTime = 0;
}

- (void)runAnimation:(CADisplayLink *)displayLink {
    if (self.style == MagicAnimationStyleRoll) {
        CFTimeInterval thisTime = CACurrentMediaTime();
        self.progressTime       = self.progressTime + (thisTime - self.lastTime);
        self.lastTime           = thisTime;
        CGFloat curNum          = [self currentNumber];
        CGFloat curPer          = [self currentPercent];
        
        if (self.progressTime >= self.duration) {
            curNum = self.endNumber;
            curPer = 1;
            if(self.progressBlock) self.progressBlock(curNum,curPer);
            return;
        }
        if(self.progressBlock) self.progressBlock(curNum,curPer);
    }
}

/**
 计算数字
 */
- (CGFloat)currentPercent {
    return  self.duration > 0 ? (self.progressTime / self.duration) : 1;
}

- (CGFloat)currentNumber {
    if (self.duration > 0) {
        CGFloat percent = [self currentPercent];
        return self.startNumber + (self.bufferFunction(percent) * (self.endNumber - self.startNumber));
    }else {
        return self.endNumber;
    }
}

- (void)setAnimationType:(MagicAnimationType)type {
    switch (type) {
        case MagicAnimationTypeEaseInOut:
            self.bufferFunction = FunctionEaseInOut;
            break;
        case MagicAnimationTypeEaseIn:
            self.bufferFunction = FunctionEaseIn;
            break;
        case MagicAnimationTypeEaseOut:
            self.bufferFunction = FunctionEaseOut;
            break;
        case MagicAnimationTypeLinear:
            self.bufferFunction = FunctionLinear;
            break;
        default:
            break;
    }
}

#pragma mark - 缓冲动画函数

CGFloat FunctionEaseOut(CGFloat p) {
    return (p == 1.0) ? p : 1 - pow(2, -10 * p);
}

CGFloat FunctionEaseIn(CGFloat p) {
    return (p == 0.0) ? p : pow(2, 10 * (p - 1));
}

CGFloat FunctionEaseInOut(CGFloat p) {
    if(p == 0.0 || p == 1.0) return p;
    
    if(p < 0.5) {
        return 0.5 * pow(2, (20 * p) - 10);
    } else {
        return -0.5 * pow(2, (-20 * p) + 10) + 1;
    }
}

CGFloat FunctionLinear(CGFloat p) {
    return p;
}

#pragma mark - 方法调用
/**
 数字滚动
 @param toNumber 结束数字
 @param type 动画类型
 */
- (void)animation_rollToNumber:(CGFloat)toNumber
                 animationType:(MagicAnimationType)type
                      duration:(CFTimeInterval)duration
                 progressBlock:(void (^)(CGFloat number,CGFloat percent))progressBlock {
    [self animation_rollFromNumber:0
                          toNumber:toNumber
                     animationType:type
                          duration:duration
                     progressBlock:progressBlock];
}

/**
 数字滚动
 @param fromNumber 开始数字
 @param toNumber 结束数字
 @param type 动画类型
 */
- (void)animation_rollFromNumber:(CGFloat)fromNumber
                        toNumber:(CGFloat)toNumber
                   animationType:(MagicAnimationType)type
                        duration:(CFTimeInterval)duration
                   progressBlock:(void (^)(CGFloat number,CGFloat percent))progressBlock {
    if (self.displayLink) [self stopDisplayLink];
    self.style = MagicAnimationStyleRoll;
    self.duration = duration;
    self.startNumber = fromNumber;
    self.endNumber = toNumber;
    self.progressBlock = progressBlock;
    [self setAnimationType:type];
    self.lastTime = CACurrentMediaTime();
    [self startDisplayLink];
}


@end
